package io.adrainty.demo.seata.storage.tcc.impl;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import io.adrainty.demo.seata.account.feign.IAccountFeign;
import io.adrainty.demo.seata.account.models.entity.AccountEntity;
import io.adrainty.demo.seata.storage.mapper.StorageMapper;
import io.adrainty.demo.seata.storage.models.bo.StorageBO;
import io.adrainty.demo.seata.storage.tcc.IStorageTCC;
import io.seata.rm.tcc.api.BusinessActionContext;
import io.seata.rm.tcc.api.BusinessActionContextUtil;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.util.Objects;

/**
 * <p>StorageTCCImpl</p>
 *
 * @author AdRainty
 * @version V1.0.0
 * @description IStorageTCC
 * @since 2025/8/21 15:20:44
 */

@Slf4j
@Component
public class StorageTCCImpl implements IStorageTCC {

    @Resource
    private IAccountFeign accountFeign;

    @Resource
    private StorageMapper storageMapper;

    @Override
    public boolean prepare(BusinessActionContext actionContext, String accountNo, Long productId, Long skuId, Integer count) {
        String xid = actionContext.getXid();
        log.info("storage prepare, xid: {}, accountNo: {}, productId: {}, skuId: {}, count: {}", xid, accountNo, productId, skuId, count);

        Wrapper<StorageBO> wrapper = Wrappers.lambdaQuery(StorageBO.class)
                .eq(StorageBO::getProductId, productId)
                .eq(StorageBO::getSkuId, skuId);
        StorageBO storageBO = storageMapper.selectOne(wrapper);
        if (storageBO == null) {
            throw new RuntimeException("商品不存在");
        }

        if (storageBO.getStock() < count) {
            throw new RuntimeException("库存不足");
        }

        // 冻结库存
        wrapper = Wrappers.lambdaUpdate(StorageBO.class)
                .eq(StorageBO::getProductId, productId)
                .eq(StorageBO::getSkuId, skuId)
                .eq(StorageBO::getStock, storageBO.getStock())
                .setSql("stock = stock - {0}", count)
                .setSql("frozen_stock = frozen_stock + {0}", count);
        boolean updated = storageMapper.update(wrapper) > 0;
        if (!updated) {
            throw new RuntimeException("库存不足");
        }

        // 冻结账户余额
        BigDecimal needDecrementBalance = storageBO.getPrice().multiply(new BigDecimal(count));
        AccountEntity accountEntity = AccountEntity.builder()
                .accountNo(accountNo)
                .accountBalance(needDecrementBalance)
                .build();
        accountFeign.frozenBalance(accountEntity);
        BusinessActionContextUtil.addContext("needDecrementBalance", needDecrementBalance);

        return Boolean.TRUE;
    }

    @Override
    public boolean commit(BusinessActionContext actionContext) {
        String xid = actionContext.getXid();

        // 获取参数
        String accountNo = Objects.requireNonNull(actionContext.getActionContext("accountNo")).toString();
        Long productId = Long.parseLong(Objects.requireNonNull(actionContext.getActionContext("productId")).toString());
        Long skuId = Long.parseLong(Objects.requireNonNull(actionContext.getActionContext("skuId")).toString());
        Integer count = Integer.parseInt(Objects.requireNonNull(actionContext.getActionContext("count")).toString());
        BigDecimal needDecrementBalance = (BigDecimal) actionContext.getActionContext("needDecrementBalance");
        log.info("storage commit, xid: {}, accountNo: {}, productId: {}, skuId: {}, count: {}", xid, accountNo, productId, skuId, count);

        // 释放库存
        Wrapper<StorageBO> wrapper = Wrappers.lambdaUpdate(StorageBO.class)
                .eq(StorageBO::getProductId, productId)
                .eq(StorageBO::getSkuId, skuId)
                .setSql("frozen_stock = frozen_stock - {0}", count);
        boolean updated = storageMapper.update(wrapper) > 0;
        if (!updated) return false;

        // 释放账户余额
        AccountEntity accountEntity = AccountEntity.builder()
                .accountNo(accountNo)
                .accountBalance(needDecrementBalance)
                .build();
        return accountFeign.addAccountBalance(accountEntity);
    }

    @Override
    public boolean rollback(BusinessActionContext actionContext) {
        // 获取参数
        try {
            String accountNo = Objects.requireNonNull(actionContext.getActionContext("accountNo")).toString();
            Long productId = Long.parseLong(Objects.requireNonNull(actionContext.getActionContext("productId")).toString());
            Long skuId = Long.parseLong(Objects.requireNonNull(actionContext.getActionContext("skuId")).toString());
            Integer count = Integer.parseInt(Objects.requireNonNull(actionContext.getActionContext("count")).toString());
            BigDecimal needDecrementBalance = (BigDecimal) actionContext.getActionContext("needDecrementBalance");
            log.info("storage rollback, accountNo: {}, productId: {}, skuId: {}, count: {}", accountNo, productId, skuId, count);

            // 恢复库存
            Wrapper<StorageBO> wrapper = Wrappers.lambdaUpdate(StorageBO.class)
                    .eq(StorageBO::getProductId, productId)
                    .eq(StorageBO::getSkuId, skuId)
                    .setSql("frozen_stock = frozen_stock - {0}", count)
                    .setSql("stock = stock + {0}", count);
            boolean updated = storageMapper.update(wrapper) > 0;
            if (!updated) return false;

            // 恢复账户余额
            // 释放账户余额
            AccountEntity accountEntity = AccountEntity.builder()
                    .accountNo(accountNo)
                    .accountBalance(needDecrementBalance)
                    .build();
            return accountFeign.addAccountBalance(accountEntity);
        } catch (Exception e) {
            log.error("storage rollback error", e);
            return false;
        }
    }

}
